--[[WorldRenderer
世界渲染器类，
用于整个世界的渲染
]]
WorldRenderer = class(WorldAccess)

function WorldRenderer:init()
    WorldAccess.init(self)
    world:setWorldRenderer(self)
    self.chunksLength = GameSetting.ViewChunkDistance
    self.chunksHeight = Chunk.Height // 16
    self.chunkCount = self.chunksLength * self.chunksLength * self.chunksHeight
    self.chunkOffset = (self.chunksLength * self.chunksLength * self.chunksHeight + self.chunksLength * self.chunksHeight) // 2 + 1
    self.chunks = {}
    
    self.t = Tesselator()
    self.t:bindTex(Tex)
    if GameSetting.PreLoadChunk then
        self.maxRebuild = 6
    else
        self.maxRebuild = 1
    end
    self.rebuildStopped = false
    self.lastUpdateTime = 0
    self.toRebuildChunkList = {}
    self.needUpdateRCList = true
    self.viewDistance = GameSetting.ViewChunkDistance * GameSetting.ViewChunkDistance * 64 * GameSetting.ViewDistanceScale
end

-- 从区块坐标取渲染区块
function WorldRenderer:getRenderChunkFromChunkCoord(x,y,z)
    return self.chunks[x * self.chunksLength * self.chunksHeight + z * self.chunksHeight + y + self.chunkOffset]
end

-- 从世界方块坐标取渲染区块
function WorldRenderer:getRenderChunkFromBlockCoord(x,y,z)
    return self:getRenderChunkFromChunkCoord(x // 16,y // 16,z // 16)
end

function WorldRenderer:hasRenderChunk(x,y,z)
    if self.chunks[x * self.chunksLength * self.chunksHeight + z * self.chunksHeight + y + self.chunkOffset] then
        return true
    else
        return false
    end
end

function WorldRenderer:bindChunk(chunk)
    local x,z = chunk.x,chunk.z
    for y = 0,self.chunksHeight - 1 do
        self.chunks[x * self.chunksLength * self.chunksHeight + z * self.chunksHeight + y + self.chunkOffset] = RenderChunk(chunk,y)
    end
    self.needUpdateRCList = true
end

function WorldRenderer:unbindChunk(x,z)
    local chunk
    for y = 0,self.chunksHeight - 1 do
        chunk = self.chunks[x * self.chunksLength * self.chunksHeight + z * self.chunksHeight + y + self.chunkOffset]
        if chunk then
            chunk:unbind()
            self.needUpdateRCList = true
        end
    end
end

function WorldRenderer:updateRenderChunks()
    if world.chunkManager.hasEnoughChunk then return end
    local x,z = player.lastPos.x // 16,player.lastPos.z // 16
    local x0,x1,z0,z1 = x - self.chunksLength // 2,x + self.chunksLength // 2,z - self.chunksLength // 2,z + self.chunksLength // 2
    local h = self.chunksHeight - 1
    for k,chunk in pairs(self.chunks) do
        if (not chunk.binded) and (not chunk.rebuilding) then
            self.chunks[k] = nil
        end
    end
end

-- 渲染
function WorldRenderer:render(frustum,layer,eyePos)
    local pos = player
    local vd = self.viewDistance
    for _,chunk in pairs(self.chunks) do
        if chunk.ready and frustum:isVisible(chunk.aabb) and chunk:distanceToSqr(pos) <= vd then
            chunk:render(layer,eyePos)
        end
    end
end

-- 获得全部需要更新的区块
function WorldRenderer:updateToRebuildChunkList()
    self.toRebuildChunkList = {}
    local pos = player
    local insert = table.insert
    if GameSetting.PreLoadChunk then
        for _,chunk in pairs(self.chunks) do
            if chunk.binded and chunk.dirty then
                insert(self.toRebuildChunkList,chunk)
            end
        end
    else
        local vd = self.viewDistance
        for _,chunk in pairs(self.chunks) do
            if chunk.binded and chunk.dirty and chunk:distanceToSqr(pos) <= vd then
                insert(self.toRebuildChunkList,chunk)
            end
        end
    end
    local function df(a,b)
        return (a:distanceToSqr(pos) < b:distanceToSqr(pos))
    end
    table.sort(self.toRebuildChunkList,df)
end

-- 更新区块
function WorldRenderer:rebuild()
    if self.rebuildStopped then
        self.rebuildStopped = false
        return
    end
    --[[
    if GameSetting.PreLoadChunk then
        if GameState == Gaming and ElapsedTime - self.lastUpdateTime < 15 then
            return
        end
        self.lastUpdateTime = ElapsedTime
    end
      ]]
    self:updateRenderChunks()
    if self.needUpdateRCList then
        self:updateToRebuildChunkList()
    end
    local count = 0
    for i,v in ipairs(self.toRebuildChunkList) do
        if count >= self.maxRebuild then return end
        if v.binded then
            v:rebuild()
            count = count + 1
            self.toRebuildChunkList[i] = nil
            if self.needUpdateRCList then return end
            Thread.waitWhenGaming()
        end
    end
end

function WorldRenderer:getRebuildCount()
    if GameSetting.PreLoadChunk then
        return #self.toRebuildChunkList
    else
        return 0
    end
end

-- 方块更新处理(WorldAccess类方法)
function WorldRenderer:blockChangedInRange(x0,y0,z0,x1,y1,z1)
    x0 = x0 // 16
    y0 = y0 // 16
    z0 = z0 // 16
    x1 = x1 // 16
    y1 = y1 // 16
    z1 = z1 // 16

    --[[
    if(x0 < 0) then x0 = 0 end
    if(y0 < 0) then y0 = 0 end
    if(z0 < 0) then z0 = 0 end
    if(x1 > self.xChunks - 1) then x1 = self.xChunks - 1 end
    if(y1 > self.yChunks - 1) then y1 = self.yChunks - 1 end
    if(z1 > self.zChunks - 1) then z1 = self.zChunks - 1 end
      ]]
    local chunk
    -- local insert = table.insert
    for x = x0,x1 do
        for y = y0,y1 do
            for z = z0,z1 do
                chunk = self:getRenderChunkFromChunkCoord(x,y,z)
                if chunk then
                    chunk.dirty = true
                    self.needUpdateRCList = true
                    -- insert(self.toRebuildChunkList,1,chunk)
                end
            end
        end
    end
end

-- 渲染选择方块
function WorldRenderer:renderSelect()
    local b = player.selectBlock
    local x,y,z,f = b.x,b.y,b.z,b.f
    local t = self.t
    if player.destoryingTime > 0 then
        local tex = 246 + math.floor(player.destoryingTime / b.block.destoryTime * 10)
        blendMode(DST_COLOR,ONE_MINUS_SRC_ALPHA)
        -- 计算破坏的贴图
        t:begin()
        t:bindTex(Tex)
        t:color4b(255,255,255,200)
        Block.renderTexture(t,x,y,z,Front,tex)
        Block.renderTexture(t,x,y,z,Back,tex)
        Block.renderTexture(t,x,y,z,Top,tex)
        Block.renderTexture(t,x,y,z,Bottom,tex)
        Block.renderTexture(t,x,y,z,Left,tex)
        Block.renderTexture(t,x,y,z,Right,tex)
        t:setShader(shader("Project:TextureShader"))
        t:End()
        t:render()
    end
    blendMode(NORMAL)
    t:begin()
    t:color4b(255,255,255,35)
    Block.renderFaceNoTexture(t,x,y,z,Front)
    Block.renderFaceNoTexture(t,x,y,z,Back)
    Block.renderFaceNoTexture(t,x,y,z,Top)
    Block.renderFaceNoTexture(t,x,y,z,Bottom)
    Block.renderFaceNoTexture(t,x,y,z,Left)
    Block.renderFaceNoTexture(t,x,y,z,Right)
    t:setShader(shader("Project:ColorMeshShader"))
    t:End()
    t:render()
end

function WorldRenderer:preventRebuild()
    self.rebuildStopped = true
end

function WorldRenderer:rebuildAllChunk()
    for i,chunk in pairs(self.chunks) do
        chunk.dirty = true
    end
end